// @Author EthanScriptOn
// @Desc
package identity

import (
	"errors"
	"gitee.com/fatzeng/srf_switch_basic_components/tool"
	"sync/atomic"
	"time"
)

// SnowflakeConfig Simple snowflake algorithm configuration
type SnowflakeConfig struct {
	Epoch        time.Time
	SequenceBits uint8
}

type SnowflakeID uint64

// Snowflake Identity Information - Snowflake Algorithm Generator
type Snowflake struct {
	config        *SnowflakeConfig
	sequence      uint64
	maxSequence   uint64
	lastTimestamp uint64
}

func GenerateSnowflake(config *SnowflakeConfig) (*Snowflake, error) {
	if config.SequenceBits > 12 {
		return nil, errors.New("sequence bits exceed the available space")
	}
	return &Snowflake{
		config:        config,
		sequence:      0,
		maxSequence:   (1 << config.SequenceBits) - 1,
		lastTimestamp: 0,
	}, nil
}

// GenerateUniqueIdentifier Generate an identity
func (s *Snowflake) GenerateUniqueIdentifier() (identifier interface{}, err error) {
	defer tool.HandleErr(&err)
	var epochTime = uint64(s.config.Epoch.UnixNano() / int64(time.Millisecond))
	var now = uint64(time.Now().UnixNano()/int64(time.Millisecond)) - epochTime
	for now <= s.lastTimestamp {
		time.Sleep(time.Nanosecond)
		now = uint64(time.Now().UnixNano()/int64(time.Millisecond)) - epochTime
	}
	s.lastTimestamp = now

	var seq uint64
	for {
		seq = atomic.LoadUint64(&s.sequence)
		if seq >= s.maxSequence {
			if atomic.CompareAndSwapUint64(&s.sequence, seq, 0) {
				seq = 0
				break
			}
			continue
		}
		oldSeq := seq
		seq = seq + 1
		if atomic.CompareAndSwapUint64(&s.sequence, oldSeq, seq) {
			break
		}
	}

	id := (now << s.config.SequenceBits) | seq
	return SnowflakeID(id), nil
}
